import axios from 'axios'
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'

// 创建错误类型
export class HttpError extends Error {
  constructor(public response: AxiosResponse) {
    super(`HTTP Error: ${response.status} ${response.statusText}`)
    this.name = 'HttpError'
  }
}

// 业务错误类型
export class ApiError extends Error {
  constructor(
    public code: number,
    public data: unknown,
    message: string,
  ) {
    super(message)
    this.name = 'ApiError'
  }
}

// 响应数据接口
interface ResponseData<T = unknown> {
  code: number
  data: T
  message: string
}

// 请求参数类型
export type RequestParams = Record<
  string,
  string | number | boolean | null | undefined
>

// 默认配置
const DEFAULT_CONFIG: AxiosRequestConfig = {
  baseURL: import.meta.env.DEV ? '/api' : import.meta.env.VITE_APP_API_URL,
  timeout: 10000, // 10秒超时
  headers: {
    'Content-Type': 'application/json',
  },
}

// 请求实例
class HttpRequest {
  private instance: AxiosInstance

  constructor(config: AxiosRequestConfig = {}) {
    this.instance = axios.create({
      ...DEFAULT_CONFIG,
      ...config,
    })

    this.setupInterceptors()
  }

  // 配置拦截器
  private setupInterceptors() {
    // 请求拦截器
    this.instance.interceptors.request.use(
      (config) => {
        // 从本地存储获取 token
        const token = localStorage.getItem('token')

        // 如果有 token 则添加到请求头
        if (token && config.headers) {
          config.headers['Authorization'] = `Bearer ${token}`
        }

        return config
      },
      (error) => {
        return Promise.reject(error)
      },
    )

    // 响应拦截器
    this.instance.interceptors.response.use(
      (response) => {
        const { data } = response

        // 判断业务状态码
        if (data && typeof data === 'object' && 'code' in data) {
          const res = data as ResponseData

          // 成功
          if (res.code === 200 || res.code === 0) {
            return res.data
          }

          // 处理登录过期、无权限等特殊情况
          if (res.code === 401) {
            // 清除登录信息
            localStorage.removeItem('token')
            // 跳转到登录页
            // window.location.href = '/login'
          }

          // 抛出业务错误
          return Promise.reject(data)
        }

        // 直接返回简单响应
        return response.data.data
      },
      (error) => {
        // 处理请求超时
        if (error.message.includes('timeout')) {
          console.error('请求超时')
        }

        // 处理取消请求
        if (axios.isCancel(error)) {
          console.log('请求已取消')
          return Promise.reject(error)
        }

        // 处理 HTTP 错误
        if (error.response) {
          return Promise.reject({
            code: error.response.status,
            message: error.response.statusText,
            data: null,
          })
        }

        // 网络错误或其他错误
        console.error('网络错误', error)
        return Promise.reject(error)
      },
    )
  }

  // 通用请求方法
  public request<T = unknown>(config: AxiosRequestConfig): Promise<T> {
    return this.instance.request(config)
  }

  // GET 请求
  public get<T = unknown>(
    url: string,
    params?: RequestParams,
    config: AxiosRequestConfig = {},
  ): Promise<T> {
    return this.instance.get(url, { params, ...config })
  }

  // POST 请求
  public post<T = unknown>(
    url: string,
    data?: unknown,
    config?: AxiosRequestConfig,
  ): Promise<T> {
    return this.instance.post(url, data, config)
  }

  // PUT 请求
  public put<T = unknown>(
    url: string,
    data?: unknown,
    config?: AxiosRequestConfig,
  ): Promise<T> {
    return this.instance.put(url, data, config)
  }

  // DELETE 请求
  public delete<T = unknown>(
    url: string,
    params?: RequestParams,
    config: AxiosRequestConfig = {},
  ): Promise<T> {
    return this.instance.delete(url, { params, ...config })
  }

  // 创建取消令牌
  public getCancelToken() {
    return axios.CancelToken.source()
  }

  // 判断是否为取消错误
  public isCancel(error: unknown): boolean {
    return axios.isCancel(error)
  }
}

// 默认导出一个实例
const http = new HttpRequest()
export default http

// 导出创建自定义实例的工厂方法
export const createHttp = (config?: AxiosRequestConfig) =>
  new HttpRequest(config)
